package com.yc.springmvc.web;

import com.google.gson.Gson;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

@WebServlet(name = "MyServlet", value = "/MyServlet")
public class MyDispatcherServlet extends HttpServlet {

    Map<String, Handler> handlerMappings = new HashMap<>();

    @Override
    public void init() throws ServletException {
        // 初始化 IOC 容器
        // 定义扫描到的控制器
        Object[] controllers = {new HelloAction(), new TestController()};
        // HanlderMapping 集合   Map<String(URL), Object>
        for (Object controller : controllers) {
            // 获取地址前缀
            String pathPrefix = "";
            RequestMapping requestMapping = controller.getClass().getAnnotation(RequestMapping.class);
            if (requestMapping != null) {
                // 取第一个地址
                pathPrefix = requestMapping.value()[0];
                if (pathPrefix.startsWith("/") == false) {
                    pathPrefix = "/" + pathPrefix;
                }
            }
            // 获取所有定义的方法
            Method[] methods = controller.getClass().getDeclaredMethods();
            for (Method method : methods) {
                // 只处理 @RequestMapping 注解
                RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                if (annotation != null) {
                    String path = null;
                    path = annotation.value().length > 0 ? annotation.value()[0] : annotation.path()[0];
                    path = pathPrefix + (path.startsWith("/") ? "" : "/") + path;
                    handlerMappings.put(path, new Handler(controller, method));
                }
            }
        }
        handlerMappings.forEach((key, value) -> {
            System.out.printf("%s : %s \n", key, value.method);
        });

    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        boolean[] handled = {false};

        handlerMappings.forEach((path,mapping)->{
            path = path.replaceAll("\\*\\*","(\\\\w+/)*\\\\w+");
            path = path.replaceAll("\\{","\\\\{");
            // 拼接上下文路径
            String contextPath = req.getServletContext().getContextPath();
            path = contextPath + path;
            System.out.printf("%s => %s \n", req.getRequestURI(), path);

            // 地址匹配
            if(req.getRequestURI().matches(path)){
                // 获取方法参数
                Parameter[] parameters = mapping.method.getParameters();
                // 参数值数组
                Object[] params = new Object[parameters.length];
                int i=0;
                for (Parameter parameter : parameters) {
                    // 获取参数名
                    RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                    String paramName = requestParam.value();
                    // 获取请求参数名称
                    String reqValue = req.getParameter(paramName);
                    System.out.println("paramName = " + paramName);
                    System.out.println("reqValue = " + reqValue);
                    // 转换类型 (简单数据类型)
                    if(parameter.getType().isAssignableFrom(String.class)){
                        params[i] = reqValue;
                    } else if(parameter.getType().isAssignableFrom(Integer.class)||
                            parameter.getType().isAssignableFrom(int.class)){
                        Integer intValue = Integer.valueOf(reqValue);
                        params[i] = intValue;
                    }
                    i++;
                }
                try {
                    Object res = mapping.method.invoke(mapping.controller, params);
                    if(mapping.controller.getClass().getAnnotation(RestController.class)!=null
                        || mapping.controller.getClass().getAnnotation(Controller.class)!=null
                            && mapping.method.getAnnotation(ResponseBody.class)!=null){
                        // json
                        Gson gson = new Gson();
                        String j = gson.toJson(res);
                        resp.setContentType("application/json;charset=utf-8");
                        resp.getWriter().append(j);
                        handled[0] = true;
                    }
                } catch (Exception e) {
                    throw new RuntimeException("执行控制器方法失败!", e);
                }

            }
        });

        if(handled[0] == false){
            // 没有匹配到 控制器
            // 作为静态请求处理
            // 将请求的资源地址, 转换为文件地址
            ServletContext servletContext = req.getServletContext();

            // 去除 路径中的 上下文路径, 仅限于在idea中解决路径的问题
            String requestURI = req.getRequestURI();
            requestURI = requestURI.replace(servletContext.getContextPath(),"");

            // 获取资源的文件路径
            String realPath = servletContext.getRealPath(requestURI);

            System.out.printf("%s ===> %s \n", requestURI, realPath);

            File file = new File(realPath);
            if(file.exists()){
                // 返回文件内容
                resp.setStatus(200);
                // 根据文件名获取 contentType
                String contextType = servletContext.getMimeType(file.getName());
                resp.setContentType(contextType);
                ServletOutputStream out = resp.getOutputStream();
                try(FileInputStream fis = new FileInputStream(file)){
                    byte[] bytes = new byte[1024];
                    int count;
                    while((count=fis.read(bytes))>0){
                        out.write(bytes,0,count);
                    }
                }
            } else {
                resp.setStatus(404);
                resp.setContentType("text/html;charset=utf-8");
                resp.getWriter().append("404错误, 访问的目标资源不存在! " + req.getRequestURI());
            }
        }
    }
}

class Handler {
    Object controller; // 控制器
    Method method;     // 方法对象

    public Handler(Object controller, Method method) {
        this.controller = controller;
        this.method = method;
    }
}
